﻿#region Header File
//
// Program.cs - Entry point 
//
// Copyright (C) Javier Valcarce. BSD License
#endregion

#region Using Statements
using System;
using System.IO;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Arq;
using NDesk.Options;
#endregion

namespace TestArq
{
    class Program
    {
        // options
        static double opt_ber      = 0;        
        static bool   opt_help     = false;
        static bool   opt_master   = false;
        static bool   opt_slave    = false;
        static string opt_com      = "";
        static int    opt_baudrate = 9600;
        static int    opt_npkt     = -1;



        static void Main(string[] args)
        {
            TimeSpan ts;
            DateTime t0;
            DateTime t1;

            ProcessArgs(args);

            t0 = DateTime.Now;

            /**/ if (opt_master)
                TestEchoMaster();
            else if (opt_slave)
                TestEchoRelay();

            Console.WriteLine("");           
            Console.ReadKey();

            //TestArq test = new TestArq();
            //test.Test2PNT(bce, bce, 64, npkt);
            //test.TestUC(com, bce, npkt);            
            //test.TestUCRX(bce1, npkt);

            t1 = DateTime.Now;
            ts = t1 - t0;
            Console.WriteLine("");
            Console.WriteLine("  Execution time was {0} s", ts.TotalSeconds);            
        }


        /// <summary>
        /// process command-line parameters and fill static variables of this class
        /// </summary>
        /// <param name="args"></param>
        static void ProcessArgs(string[] args)
        {
            OptionSet p = new OptionSet()
              .Add("h|help",    delegate(string v) { if (v != null) opt_help     = true; })
              .Add("com=",      delegate(string v) { if (v != null) opt_com      = v;    })
              .Add("baudrate=", delegate(string v) { if (v != null) opt_baudrate = int.Parse(v);    })
              .Add("ber=",      delegate(string v) { if (v != null) opt_ber      = double.Parse(v); })
              .Add("slave",     delegate(string v) { if (v != null) opt_slave    = true; })
              .Add("master",    delegate(string v) { if (v != null) opt_master   = true; });
            List<string> extra = p.Parse(args);

            if (opt_help ||extra.Count != 1)
            {
                Help();
                return;
            }

            opt_npkt = int.Parse(extra[0]);
        }


        /// <summary>
        /// Simple Test. Includes byte channel
        /// </summary>
        /// <param name="args"></param>
        static void TestEchoMaster()
        {
            Random     rnd0 = new Random();
            SerialPort com0 = new SerialPort(opt_com, opt_baudrate);
            
            com0.Open();
            
            FiniteQueueStream arc1 = new FiniteQueueStream(16);
            FiniteQueueStream arc2 = new FiniteQueueStream(16);
            StopAndWait       arq0 = new StopAndWait(arc1, arc2);
            ByteChannel       bc1  = new ByteChannel(rnd0, opt_ber, arc2, com0.BaseStream);
            ByteChannel       bc2  = new ByteChannel(rnd0, opt_ber, com0.BaseStream, arc1);

            byte[] txbuf = new byte[StopAndWait.MTU];
            byte[] rxbuf = new byte[StopAndWait.MTU];
            int rxlen;
            int txlen;
            bool equal;
            Console.WriteLine("Echo test. Tx/Rx {0} packets...", opt_npkt);

            arq0.LinkDown += new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine("\n--> link down "); });
            arq0.LinkUp   += new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine("\n--> link up   "); });

            for (int i = 0; i < opt_npkt; i++)
            {
                // fill pkt of random size with with random bytes
                rnd0.NextBytes(txbuf);
                txlen = rnd0.Next(1, StopAndWait.MTU + 1);

                arq0.SendPacket(txbuf, 0, /**/txlen);
                arq0.RecvPacket(rxbuf, 0, out rxlen);

                // ---------------------------------------------------------
                // check that the transmitted and received packets are equal
                equal = true;
                if (txlen != rxlen)
                    equal = false;
                else
                {
                    for (int j = 0; j < txlen; j++)
                        if (txbuf[j] != rxbuf[j])
                        {
                            equal = false;
                            break;
                        }
                }
                // ---------------------------------------------------------

                if (!equal)
                {                    
                    Console.WriteLine("\nERROR. TX/RX packet are not equal. Continuing...");
                    Console.WriteLine("txlen = {0} rxlen = {1}", txlen, rxlen);
                    Console.ReadKey();
                }
                else
                    Console.Write("\r  Pkt={0,4} E[Rt]={1,5:f2} TxRate={2,8:f2}bps",
                        i, arq0.AvrReTxPerPacket, arq0.TxStats.AverageDataRate);
            }
        }


        /// <summary>
        /// Simple Test
        /// </summary>
        /// <param name="args"></param>
        static void TestEchoRelay()
        {
            SerialPort com0 = new SerialPort(opt_com, opt_baudrate);
            com0.Open();
            StopAndWait arq0 = new StopAndWait(com0.BaseStream, com0.BaseStream);
            byte[] pkt;

            Console.WriteLine("Echo test. Relay {0} packets...", opt_npkt);
            arq0.LinkDown += new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine("\n--> link down "); });
            arq0.LinkUp   += new EventHandler(delegate(object sender, EventArgs e) { Console.WriteLine("\n--> link up   "); });

            for (int i = 0; i < opt_npkt; i++)
            {
                arq0.RecvPacket(out pkt);
                arq0.SendPacket(ref pkt);
                
                Console.Write("\r  Pkt={0,4} E[Rt]={1,5:f2} TxRate={2,8:f2}bps",
                    i, arq0.AvrReTxPerPacket, arq0.TxStats.AverageDataRate);
            }
        }

        static void Help()
        {
            Console.WriteLine("TestArq.exe [--com=COM1 --baudrate=9600 --master --slave] <npkts>");
            Environment.Exit(1);
        }
    }
}
